package za.org.jcicada.cfgmngr.impl;

import java.util.EnumMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import za.org.jcicada.cfgmngr.ISystemObserver;
import za.org.jcicada.cfgmngr.ISystemSubject;
import za.org.jcicada.cfgmngr.SystemSetType;
import za.org.jcicada.cfgmngr.ISystemObserver.Event;
import za.org.jcicada.genss.SystemURN;
import za.org.jcicada.genss.messages.Result;
import za.org.jcicada.genss.messages.SuccessCode;

/**
 * Implementation of the IConfigurationSubject interface. It keeps track of which
 * observers are attached to which systems.
 * @author rcrida
 */
public class SystemSubject implements ISystemSubject {

	/** Object used for logging messages */
	private static final Log logger = LogFactory.getLog(SystemSubject.class);
	
	/** Set containing attached observers */
	protected Set<ISystemObserver> observers = new HashSet<ISystemObserver>();
	
    /** Set of all registered system URIs that this subject knows about. */
    protected Map<SystemSetType,Set<SystemURN>> systemURNs = new EnumMap<SystemSetType,Set<SystemURN>>(SystemSetType.class);
    
    /** Service URN for this service */
    public static final String URN_SERVICE = "systems";
    
    public SystemSubject() {
    	systemURNs.put(SystemSetType.REGISTERED, new HashSet<SystemURN>());
    	systemURNs.put(SystemSetType.VIRTUAL, new HashSet<SystemURN>());
    }
    
    public void notifySystemEvent(SystemURN systemURN, SystemSetType type, Event event) {
    	logger.info(String.format("%s %s %s", systemURN, type, event));
    	if (event == Event.REGISTERED) {
    		systemURNs.get(type).add(systemURN);
    	} 
    	for (ISystemObserver observer : observers) {
    		observer.informSystemEvent(systemURN, type, event);
    	}
    	if (event == Event.DEREGISTERED){
    		systemURNs.get(type).remove(systemURN);
    	}
    }
    
	public void informDisconnect(ISystemObserver observer) {
		detachObserver(observer);
	}

	public void requestAttach(ISystemObserver observer) {
		Result result;
		if (observers.contains(observer)) {
			String message = "Observer is already attached to subject";
			result = new Result(SuccessCode.PARTIAL_SUCCESS, message);
			logger.error(message);
		} else {
			result = new Result();
			observers.add(observer);
			// Inform new observer of all registered systems
			for (Map.Entry<SystemSetType, Set<SystemURN>> entry : systemURNs.entrySet()) {
				for (SystemURN systemURN : entry.getValue()) {
					observer.informSystemEvent(systemURN, entry.getKey(), Event.REGISTERED);
				}
			}
		}
		observer.replyAttach(result);
	}

	public void requestDetach(ISystemObserver observer) {
		Result result = detachObserver(observer);
		observer.replyDetach(result);
	}

    /**
     * Detach observer from all systems
     * @param observer to be detached from all systems
     */
    private Result detachObserver(ISystemObserver observer) {
		Result result;
		if (observers.contains(observer)) {
			result = new Result();
			observers.remove(observer);
		} else {
			String message = "Specified observer not currently attached";
			result = new Result(SuccessCode.PARTIAL_SUCCESS, message);
			logger.error(message);
		}
		return result;
    }

}
